#include "server.h"
#include "log.h"
#include <SDL/SDL.h>
#include <SDL/SDL_net.h>

#include <cstring>
#include <string>

Server::Server() {
	_num_clients = 0;
	_running = 0;
	_clients = vector<Client_data>();
	_game = new Game();
};

// Initialize SDL_net, open port
int Server::initserver(int port) {
  *lg << "Initializing server on port "<< port << fl;

  int len; 

  IPaddress ip;
  Uint32 ipaddr;      
      
  if(SDL_Init(0)==-1) {
    *lg << error <<"SDL_Init: " << SDL_GetError() << fl;
		return 1;
  } 
  if(SDLNet_Init()==-1) {
    *lg << error <<"SDLNet_Init: " << SDLNet_GetError() << fl;
    SDL_Quit(); 
    return 2;
  } 
  if(SDLNet_ResolveHost(&ip,NULL,port)==-1) {
    *lg << error << "SDLNet_ResolveHost: " << SDLNet_GetError() << fl;
		SDLNet_Quit();
		SDL_Quit();
		return 3;
  } 

  ipaddr=SDL_SwapBE32(ip.host); 
      
  *lg << info << "IP Address : " <<	(ipaddr>>24) << '.' << ((ipaddr>>16)&0xff) << '.' << ((ipaddr>>8)&0xff) << '.' << (ipaddr&0xff) << fl; 
  const char * host=SDLNet_ResolveIP(&ip); 
			
  if(host) {
    *lg << info << "Hostname   : " << host << fl;
  } else {
  	*lg << info << "Hostname   : n/a" << fl;
  }

  *lg << "Port       : " << port << fl; 
			
	_server_sock=SDLNet_TCP_Open(&ip);
  if(!_server_sock) {
    *lg << error << "SDLNet_TCP_Open: " << SDLNet_GetError() << fl;
     SDLNet_Quit();
     SDL_Quit();
     return 4;
  } 
	_running = true;
  _identifier = 1;		
  *lg << "Initialization done" << fl;      
  return 0;
}


// Free server
void Server::freeserver() {
  SDLNet_Quit();
  SDL_Quit();
}

// Main socket/client-handling thread
int Server::main_server_thread() {
  SDLNet_SocketSet set; 
  TCPsocket sock; 
  *lg << debug << "Accepting connections" << fl;
  char *message;


  while(1) {
    if(!_running) {
      // should stop
      // send info to clients
      for(int j=0; j<_num_clients; j++) {
        put(_clients[j].sock, "0");
      }
      SDLNet_TCP_Close(_server_sock);
      *lg << info << "Stopping server" << fl;

      break;
    }
    int numready,i;
    set = create_sockset();
    numready = SDLNet_CheckSockets(set, (Uint32)-1);

 		if(numready==-1) {
      *lg << error << "SDLNet_CheckSockets: " << SDLNet_GetError() << fl;
      break;
    }
    if(!numready)	continue;
    // new connection		
    if(SDLNet_SocketReady(_server_sock)) {
      numready--;
    	*lg << debug << "Got connection" << fl;

      sock = SDLNet_TCP_Accept(_server_sock);

      if(sock) {
        char *ack;

        *lg << debug << "Connection accepted" << fl;
    		
        if(recv(sock, ack)) {
          *lg << debug << "Ack message recieved " << ack << fl;
              
          Client_data *client;
          char buffer[5];
          itoa(_identifier, buffer, 10);
          client = add_client(sock,atoi(buffer));
          *lg << debug << "Sending ID: " << _identifier++ << fl;
          put(sock, buffer);
          delete [] ack;
        } else {
          // free socket
          *lg << debug << "Deleting socket" << fl;
          SDLNet_TCP_DelSocket(set, sock);
        }
      }
    }
    // handle already registered clients
    for(i=0; numready && i<_num_clients; i++) {
      if(SDLNet_SocketReady(_clients[i].sock)) {
        if(recv(_clients[i].sock, message)) {
           char *str;

           numready--;
           // interpret incoming messages
		   if ((message[0]=='!') && (message[1]=='C') && strlen(message)>2) {
        char data[10];
        itoa(_clients[i].id, data, 10);
				if (str=_game->parse_proto(message+1,strlen(message+1),data)) {
					*lg<<"Server::sending "<<str<<fl;
					for(int j=0; j<_num_clients; j++) {
					    put(_clients[j].sock, str);
					}
					delete[] str;
				};
		   }
           else if(message[0]=='/' && strlen(message)>1) {
             // is command
             do_command(message,&_clients[i]);
           } else if(message[0]=='|') {
             // is autogenereated I/O datagram
             *lg << debug << "I/O: " << message << fl;
           } else {
             // is message
             *lg << debug << "Msg: " << message << fl;
             // send to all clients except the sender
             for(int j=0; j<_num_clients; j++) {
               if(i!=j) put(_clients[j].sock, message);
             }
           }

          // respond
          //put(_clients[i].sock, "2");
    		  delete [] message;
        } else {
           // bye client
           *lg << debug << "Deleting socket, removing CD struct" << fl;
           SDLNet_TCP_DelSocket(set, _clients[i].sock);
           _clients.erase(_clients.begin()+i);
           _num_clients--;
        }
      }
    }

  } // end while
}

// process incoming command
void Server::do_command(char *msg, Server::Client_data *client)  {
  *lg << info << "Command from: " << client->id << " >> " << msg << fl;
//  msg = strtok (msg,"/ ");
	msg++;
	int msg_len = strlen(msg);
	printf("msg=%s\n",msg);
  // few simple commands
	if(memcmp(msg, "quit", msg_len-1) == 0) {
		put(client->sock, "Stopping server, bye..");
		this->_running = false;
	}
	else if (memcmp(msg, "clist", msg_len-1) == 0) {
		std::vector<Client_data>::iterator it;
    char buff[1000];
		for (it=_clients.begin();it!=_clients.end();it++) {
      sprintf(buff, "%s %d\n", buff, (*it).id); 
		}
    put(client->sock, buff);   
	}
}

// terminate string on unknowm character    
void Server::fix_string(char *s)  {
  unsigned int i;
	if((i=strspn(s,"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ|_=-+.,:;/\\?!@#$%^&*()~`"))!=strlen(s))
    s[i]='\0';
} 
Server::Client_data *Server::add_client(TCPsocket sock, int id)  {
  *lg << info << "Attemp to create Client data struct" << fl;

  Client_data cd = Client_data();
  cd.id = id;
  cd.sock = sock;
	
  *lg << info << "CD struct created n:" << cd.id << fl;

  try {
		_clients.push_back(cd);
  }
  catch (...) {
    *lg << error << "?" << fl;
  };
  _num_clients++;
  	
  	
  *lg << info << "Added --> " << id << fl;
  return new Client_data(cd);
}
// create socket set
SDLNet_SocketSet Server::create_sockset() {
	static SDLNet_SocketSet set=NULL;
	int i;

	if(set)
		SDLNet_FreeSocketSet(set);
	set=SDLNet_AllocSocketSet(_num_clients+1);
	if(!set) {
		*lg << error << "SDLNet_AllocSocketSet: " << SDLNet_GetError() << fl;
		// casta chyba .. ze vraj
		exit(1);
	}
	SDLNet_TCP_AddSocket(set,_server_sock);
	for(i=0;i<_num_clients;i++)
		SDLNet_TCP_AddSocket(set,_clients[i].sock);
	return set;
}

int Server::recv(TCPsocket sock, char * &buf) {
  *lg << debug << "Server::recv" << fl;
  #define MAXLEN 1000
  int result;
  char msg[MAXLEN];
  
  memset(msg, '\0', sizeof(char) * MAXLEN);
  result=SDLNet_TCP_Recv(sock,msg,MAXLEN);
  if(result<=0) {
    if(SDLNet_GetError() && strlen(SDLNet_GetError())) 
			*lg << error << "SDLNet_TCP_Recv: " << SDLNet_GetError() << fl;
    *lg << error << "/Server::recv - Err" << fl;
    
    // An error may have occured, but sometimes you can just ignore it
    // It may be good to disconnect sock because it is likely invalid now.
    
    // so what
    *lg << error << "Socket becoming invalid" << fl;
    return 0;
    
  }
  *lg << debug << "Received: " << msg << fl;
  buf = new char[strlen(msg) + 1];
  strncpy(buf, msg, strlen(msg) + 1);

  *lg << debug << buf << "\n/Server::recv" << fl;
  return strlen(buf);
} 
 
int Server::put(TCPsocket sock, const char *buf) {
  *lg << debug << "Server::put" << fl;
	Uint32 len,result;

	if(!buf || !strlen(buf))
		return(1);
		
	// determine the length of the string
	len = strlen(buf)+1; // add one for the terminating NULL

	// send the buffer, with the NULL as well
	result = SDLNet_TCP_Send(sock, (void*)(buf), len);
	if(result<len) {
		if(SDLNet_GetError() && strlen(SDLNet_GetError())) // sometimes blank!
			*lg << error << "SDLNet_TCP_Send: " << SDLNet_GetError() << fl;
		*lg << error << "/Server::put - Err" << fl;	
		return(0);
	}

	*lg << debug << "/Server::put" << fl;
	// return the length sent
	return result;
}
